package authservice

import (
	"errors"
	"fmt"
	"slices"
	"strings"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/core/builder"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/core/builder/builderenum"
	"yunj/pkg/yunj/core/builder/form"
	"yunj/pkg/yunj/core/builder/form/button"
	"yunj/pkg/yunj/core/builder/form/field"
	"yunj/pkg/yunj/core/builder/table"
	"yunj/pkg/yunj/core/builder/table/col"
	"yunj/pkg/yunj/core/builder/table/col/action"
	"yunj/pkg/yunj/core/builder/table/deftoolbar"
	"yunj/pkg/yunj/core/builder/table/toolbar"
	"yunj/pkg/yunj/core/response"
	"yunj/pkg/yunj/util"

	"yunj/pkg/yunj/app/data"
	"yunj/pkg/yunj/app/dto"
	"yunj/pkg/yunj/app/enum/admin/authenum"
	"yunj/pkg/yunj/app/enum/admin/isdemoenum"
	"yunj/pkg/yunj/app/enum/admin/issystemenum"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/service/admin/adminservice"
	"yunj/pkg/yunj/app/validator/admin/authvalidator"
	errcodeEnum "yunj/pkg/yunj/core/enum/errcode"

	"github.com/gin-gonic/gin"
	"github.com/worklz/go-validate"
	"gorm.io/gorm"
)

type authPage struct {
}

var AuthPage *authPage

// 获取页面类型
func (s *authPage) GetPageType(ctx *gin.Context) *authenum.PageType {
	pageTypeRaw := util.QueryParam(ctx, "pageType", "")
	pageTypeValue, ok := authenum.PageTypeEnum.IsValue(pageTypeRaw)
	if !ok {
		if util.IsAsync(ctx) {
			response.AbortFail(ctx, "错误请求")
		} else {
			util.RedirectAdminTips(ctx, errcodeEnum.NOT_FOUND, "错误请求")
		}
	}
	return authenum.PageTypeByValue(pageTypeValue)
}

// 列表是否为树形
func (s *authPage) listBuilderIsTree(stateCode any) bool {
	// 回收站开启分页、不使用树形展示
	return util.Float64ToUints(stateCode) != stateenum.RECYLE_BIN
}

// 列表构建器
func (s *authPage) ListBuilder(ctx *gin.Context) *builder.Table {
	return builder.NewTable(ctx, "AuthList").
		State([]builderdto.TableState{
			{Code: "all", Title: "所有菜单&权限"},
			{Code: authenum.PAGE_TYPE_SIDEBAR_MENU, Title: "侧边栏菜单"},
			{Code: authenum.PAGE_TYPE_TOP_MENU, Title: "顶部菜单"},
			{Code: stateenum.RECYLE_BIN, Title: "回收站"},
		}).
		Pk("key").
		Page(func(t *builder.Table, state *builderdto.TableState) (res bool, err error) {
			res = !s.listBuilderIsTree(state.Code)
			return
		}).
		Tree(func(t *builder.Table, state *builderdto.TableState) (res bool, err error) {
			res = s.listBuilderIsTree(state.Code)
			return
		}).
		Filter(field.NewText("keywords", "关键词").SetPlaceholder("模糊匹配，名称/KEY").SetAuth("yunj_auth_list_filter")).
		Toolbar(func(t *builder.Table, state *builderdto.TableState) (actions []toolbar.ActionInterface, err error) {
			var pageType any
			pageType = authenum.PAGE_TYPE_NORMAL
			if slices.Contains([]any{authenum.PAGE_TYPE_SIDEBAR_MENU, authenum.PAGE_TYPE_TOP_MENU}, state.Code) {
				pageType = state.Code
			}
			actions = []toolbar.ActionInterface{
				toolbar.NewOpenPopup("add", "添加").SetClass("layui-icon-add-circle").
					SetUrl(util.BuildAdminUrl("/auth/list/add", map[string]any{"pageType": pageType})).
					SetAuth("yunj_auth_list_add"),
			}
			if state.Code == stateenum.RECYLE_BIN {
				actions = append(actions,
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_auth_list_normal"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_auth_list_deleted"),
				)
			} else {
				actions = append(actions,
					toolbar.NewOpenPopup(builderenum.TABLE_EVENT_TYPE_SORT, "树形/排序").SetClass("yunj-icon-sort").
						SetUrl(util.BuildAdminUrl("/auth/list/sort", map[string]any{"pageType": pageType})).
						SetAuth("yunj_auth_list_sort"),
					toolbar.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_auth_list_recyle_bin"),
				)
			}
			return
		}).
		DefaultToolbar([]deftoolbar.ActionInterface{
			deftoolbar.NewFilter(),
			deftoolbar.NewExport().SetAuth("yunj_auth_list_export"),
			deftoolbar.NewAsyncEvent("clearCache").SetTitle("清理缓存").SetClass("yunj-icon-clear").SetAuth("yunj_auth_list_clear_cache"),
		}).
		Cols(func(t *builder.Table, state *builderdto.TableState) (cols []table.ColInterface, err error) {
			var pageType any
			pageType = authenum.PAGE_TYPE_NORMAL
			if state.Code == authenum.PAGE_TYPE_SIDEBAR_MENU || state.Code == authenum.PAGE_TYPE_TOP_MENU {
				pageType = state.Code
			}
			cols = []table.ColInterface{
				col.NewCheckbox("key"),
				col.NewText("name", "名称/KEY"),
				col.NewEnum("is_system", "系统权限").SetOptions(builder.GetEnumTableColOptions(issystemenum.Enum)),
				col.NewJson("request_data", "请求配置"),
			}
			if pageType == authenum.PAGE_TYPE_SIDEBAR_MENU || pageType == authenum.PAGE_TYPE_TOP_MENU {
				cols = append(
					cols,
					col.NewIcon("icon", "图标"),
					col.NewEnum("page_open", "页面打开方式").SetOptions(builder.GetEnumTableColOptions(authenum.PageOpenEnum)),
				)
			} else {
				cols = append(
					cols,
					col.NewEnum("type", "类型").SetOptions(builder.GetEnumTableColOptions(authenum.TypeEnum)),
				)
			}
			actionCol := col.NewAction("操作").SetOptions([]action.ActionInterface{
				action.NewOpenPopup("edit", "详情").SetClass("layui-icon-survey").SetUrl(util.BuildAdminUrl("/auth/list/edit", map[string]any{"pageType": pageType})).SetAuth("yunj_auth_list_edit"),
			})
			if state.Code == stateenum.RECYLE_BIN {
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_NORMAL, "恢复正常").SetDropdown(true).SetAuth("yunj_auth_list_normal"),
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_DELETED, "永久删除").SetDropdown(true).SetAuth("yunj_auth_list_deleted").SetConfirmFormFields(field.NewTxt("tips", "确认删除？")),
				)
			} else {
				titleSuffix := "权限"
				if state.Code == authenum.PAGE_TYPE_SIDEBAR_MENU || state.Code == authenum.PAGE_TYPE_TOP_MENU {
					titleSuffix = "菜单"
				}
				actionCol.Options = append(actionCol.Options,
					action.NewAsyncEvent(builderenum.TABLE_EVENT_TYPE_RECYLE_BIN, "移入回收站").SetDropdown(true).SetAuth("yunj_auth_list_recyle_bin"),
				)
				actionCol.Options = append([]action.ActionInterface{
					action.NewOpenPopup("itemAdd", "添加子"+titleSuffix).SetClass("layui-icon-add-circle").SetUrl(util.BuildAdminUrl("/auth/list/add", map[string]any{"pageType": pageType, "isAddChild": "yes"})).SetAuth("yunj_auth_list_add"),
				}, actionCol.Options...)
			}
			cols = append(cols, actionCol)
			return
		}).
		Validator(func(t *builder.Table) (validate.ValidatorInterface, error) {
			v := &authvalidator.Auth{}
			v.InitValidator(v)
			return v, nil
		}).
		Count(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableCountResult, err error) {
			query := global.MySQL.Model(&model.AdminAuth{})
			var filterFormParam dto.AdminAuthPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			err = query.Count(&res.Count).Error
			if err != nil {
				err = fmt.Errorf("数据量查询异常！%v", err)
				return
			}
			return
		}).
		Items(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableItemsResult, err error) {
			query := global.MySQL.Select("*", "parent_key as pkey")
			if !s.listBuilderIsTree(requestParam.State) {
				query.Limit(requestParam.Limit()).Offset(requestParam.Offset())
			}
			var filterFormParam dto.AdminAuthPageListFilterFormParam
			util.MapToStruct(requestParam.FilterData(), &filterFormParam)
			s.listBuilderFilterQuery(query, requestParam, &filterFormParam)
			if s.listBuilderIsTree(requestParam.State) {
				orderField := "sort asc,is_system asc"
				if requestParam.State != authenum.PAGE_TYPE_SIDEBAR_MENU && requestParam.State != authenum.PAGE_TYPE_TOP_MENU {
					orderField = "type desc," + orderField
				}
				query.Order(orderField)
			} else {
				query.Order("updated_at desc")
			}
			var authItems []*model.AdminAuth
			if err = query.Find(&authItems).Error; err != nil {
				err = fmt.Errorf("数据查询异常！%v", err)
				return
			}
			res.Items, res.ExpandTreeNode, err = s.listBuilderItemsHandle(authItems, requestParam, &filterFormParam)
			return
		}).
		Event(func(t *builder.Table, requestParam *builderdto.TableRequestParam) (res builderdto.TableEventResult, err error) {
			handleEventRes, err := builderenum.HandleEvent(ctx, &model.AdminAuth{}, requestParam.EventCode, requestParam.Pks, func(param *builderenum.EventDbParam, e *builderenum.Event, pks []any) (err error) {
				// 判断是否有系统权限，系统权限不允许删除
				if e.IsStateEvent() {
					// 数据状态修改（系统权限不允许再修改）
					var existCount int64
					if err = global.MySQL.Model(&model.AdminAuth{}).Where("`key` in ?", pks).Where("is_system = ?", issystemenum.YES).Count(&existCount).Error; err != nil {
						return
					}
					if existCount > 0 {
						return errors.New("系统权限不可" + e.GetDesc())
					}
				}
				if e.Value == builderenum.TABLE_EVENT_TYPE_DELETED {
					param.Data["key"] = gorm.Expr("concat(`key`, '_del_', ?)", time.Now().Format("20060102150405"))
					param.Where = append(param.Where, []interface{}{"is_system <> ?", issystemenum.YES})
				} else if e.Value == builderenum.TABLE_EVENT_TYPE_RECYLE_BIN {
					param.Where = append(param.Where, []interface{}{"is_system <> ?", issystemenum.YES})
				}
				return
			})
			if err != nil {
				return
			}
			if handleEventRes != nil {
				if handleEventRes.Errcode == errcodeEnum.SUCCESS {
					// 清理缓存
					err = data.AuthPrepareOrReset()
					if err != nil {
						global.Logger.WithField("requestParam", requestParam).WithError(err).Error("权限管理事件处理后，清理缓存失败！")
					}
				}
				res.ResponseJsonData = handleEventRes
				return
			}
			// 清理缓存
			if requestParam.EventCode == "clearCache" {
				err = data.AuthPrepareOrReset()
				if err != nil {
					err = fmt.Errorf("清理缓存失败！%v", err)
					return
				}
				res.Reload = true
				res.Message = "缓存清理成功"
				return
			}
			err = fmt.Errorf("异常事件[%v]", requestParam.EventCode)
			return
		})
}

// 列表数据项处理
func (s *authPage) listBuilderItemsHandle(items []*model.AdminAuth, requestParam *builderdto.TableRequestParam, filterFormParam *dto.AdminAuthPageListFilterFormParam) (resItems []*dto.AdminAuthPageListItemResponse, expandTreeNode bool, err error) {
	if len(items) <= 0 {
		return
	}
	if s.listBuilderIsTree(requestParam.State) && filterFormParam.Keywords != "" {
		// 树形，且搜索条件存在，则查找返回所有上级权限
		allParentKeys := []string{}
		parentKeysSlice := util.StructSliceColumn[*model.AdminAuth, model.StrSliceJson](items, "ParentKeys")
		for _, parentKeys := range parentKeysSlice {
			if len(parentKeys) > 0 {
				allParentKeys = append(allParentKeys, parentKeys...)
			}
		}
		allParentKeys = util.SliceUnique(allParentKeys)
		if len(allParentKeys) > 0 {
			var allParentAuths []*model.AdminAuth
			allParentAuthsQuery := global.MySQL.Select("*", "parent_key as pkey").Where("`key` in ?", allParentKeys)
			orderField := "sort asc,is_system asc"
			if requestParam.State != authenum.PAGE_TYPE_SIDEBAR_MENU && requestParam.State != authenum.PAGE_TYPE_TOP_MENU {
				orderField = "type desc," + orderField
			}
			allParentAuthsQuery.Order(orderField)
			if err = allParentAuthsQuery.Find(&allParentAuths).Error; err != nil {
				err = fmt.Errorf("所有上级权限数据查询异常！%v", err)
				return
			}
			items = append(items, allParentAuths...)
			expandTreeNode = true
		}
	}

	resItems = make([]*dto.AdminAuthPageListItemResponse, 0, len(items))
	for _, item := range items {
		resItem := &dto.AdminAuthPageListItemResponse{
			Key:      item.Key,
			Pkey:     item.ParentKey,
			Name:     fmt.Sprintf("【%v】%v", item.Name, item.Key),
			IsSystem: item.IsSystem,
			Icon:     item.Icon,
			PageOpen: item.PageOpen,
			Type:     item.Type,
		}
		// request_data
		if item.RequestUrl != "" {
			resItem.RequestData = map[string]any{"Url": item.RequestUrl}

			if item.PageOpen == authenum.PAGE_OPEN_NIL {
				resItem.RequestData["Method"] = item.RequestMethod
			}
			if len(item.RequestRequireParams) > 0 {
				resItem.RequestData["必要参数"] = item.RequestRequireParams
			}
		}
		resItems = append(resItems, resItem)
	}
	return
}

// 列表过滤条件查询对象
func (s *authPage) listBuilderFilterQuery(query *gorm.DB, requestParam *builderdto.TableRequestParam, filterFormParam *dto.AdminAuthPageListFilterFormParam) {
	// keys
	if len(requestParam.Pks) > 0 {
		query.Where("`key` in ?", requestParam.Pks)
	}
	// state
	stateWhere := stateenum.NORMAL
	if util.Float64ToUints(requestParam.State) == stateenum.RECYLE_BIN {
		stateWhere = stateenum.RECYLE_BIN
	}
	query.Where("state = ?", stateWhere)
	// 是否引入demo
	if !global.Config.App.Admin.UseDemo {
		query.Where("is_demo = ?", isdemoenum.NO)
	}
	// type
	if pageTypeValue, ok := authenum.PageTypeEnum.IsValue(requestParam.State); ok {
		if pageTypeEnum := authenum.PageTypeByValue(pageTypeValue); pageTypeEnum.IsMenu() {
			query.Where("type = ?", pageTypeEnum.GetDbTypeValue())
		}
	}
	// keywords
	if filterFormParam.Keywords != "" {
		keywordsWhere := "%" + filterFormParam.Keywords + "%"
		query.Where("`key` like ? or name like ?", keywordsWhere, keywordsWhere)
	}
}

// 表单构建器
func (s *authPage) FormBuilder(ctx *gin.Context, isEdit, isAddChild bool) (formBuilder *builder.Form) {
	formBuilder = builder.NewForm(ctx, "AuthForm").
		Field(func(f *builder.Form, tab *builderdto.FormTab) (fields []form.FieldInterface, err error) {
			isEditDefault, isAddChildDefault := uint8(0), uint8(0)
			if isEdit {
				isEditDefault = uint8(1)
			}
			if isAddChild {
				isAddChildDefault = uint8(1)
			}
			pageType := s.GetPageType(ctx)
			fields = []form.FieldInterface{
				field.NewHidden("is_edit").SetVerify("in:0,1").SetDefault(isEditDefault),
				field.NewHidden("is_add_child").SetVerify("in:0,1").SetDefault(isAddChildDefault),
				field.NewDropdownSearch("parent_key", "父权限").SetReadonly(isAddChild).SetDesc("不选择则为顶级权限").SetOptionsApi(util.BuildAdminUrl("/auth/parentKeyDropdownSearchOptions")),
				field.NewText("key", "KEY").SetVerify("required|max:50").SetReadonly(isEdit).SetDesc("全局唯一标识。创建后不能更改"), // 编辑时不能修改
				field.NewText("name", "名称").SetVerify("required|max:50"),
				field.NewText("desc", "描述").SetVerify("max:200"),
				field.NewRadio("type", "类型").SetOptions(builder.GetEnumOptions(authenum.TypeEnum)).SetDefault(pageType.GetDbTypeValue()),
				field.NewIcon("icon", "图标"),
				field.NewRadio("request_mode", "添加请求信息").SetOptions(builder.GetEnumOptions(authenum.RequestModeEnum)).SetDefault(authenum.REQUEST_MODE_NIL),
				field.NewRadio("page_open", "页面打开方式").SetOptions(builder.GetEnumOptions(authenum.PageOpenEnum)).SetDefault(authenum.PAGE_OPEN_NIL),
				field.NewText("request_url", "请求地址").SetVerify("max:200"),
				field.NewSelect("request_method", "请求方式").SetOptions(builder.GetEnumOptions(authenum.RequestMethodEnum)),
				field.NewTable("request_require_params", "请求必要参数").SetAddAction(true).SetDelAction(true).SetCols([]builderdto.FormTableCol{
					{Field: "key", Title: "参数key", Verify: "required"},
					{Field: "value", Title: "参数value"},
				}),
			}
			if !isEdit {
				fields = append(fields, field.NewTreeCheckbox("create_child_auths", "创建子权限").SetNodes(authenum.GetFormTreeChildAuthNodes()))
			}

			loadData, err := builder.FormLoadValue[dto.AdminAuthPageFormLoadResponse](f)
			if err != nil {
				return
			}
			if isEdit {
				// 权限
				for _, v := range fields {
					v.SetFieldAuth("yunj_auth_edit_detail_normal")
				}
				if !adminservice.CheckAuth(ctx, "yunj_auth_edit_submit") {
					for _, v := range fields {
						v.SetFieldReadonly(true)
					}
				}
			}
			isSystem := issystemenum.NO
			if loadData.IsSystem != issystemenum.NO {
				isSystem = loadData.IsSystem
			}
			if isSystem == issystemenum.YES {
				fields = append([]form.FieldInterface{field.NewTxt("tips", "温馨提示  系统配置权限不允许修改哦！").SetColor("#FF5722")}, fields...)
				for _, v := range fields {
					v.SetFieldReadonly(true)
				}
			}
			// 设置栅格布局
			for _, v := range fields {
				v.SetFieldGrid([]any{12, 6, "6 l3 r3"})
			}
			return
		}).
		Button(func(f *builder.Form) (buttons []form.ButtonInterface, err error) {
			loadData, err := builder.FormLoadValue[dto.AdminAuthPageFormLoadResponse](f)
			if err != nil {
				return
			}
			isSystem := issystemenum.NO
			if loadData.IsSystem != issystemenum.NO {
				isSystem = loadData.IsSystem
			}
			if isSystem == issystemenum.YES {
				return
			}
			if isEdit {
				if adminservice.CheckAuth(ctx, "yunj_auth_edit_submit") {
					buttons = []form.ButtonInterface{
						button.NewReset(),
						button.NewSubmit(),
					}
				}
			} else {
				buttons = []form.ButtonInterface{
					button.NewClear(),
					button.NewSubmit(),
				}
			}
			return
		}).
		Validator(func(f *builder.Form) (validate.ValidatorInterface, error) {
			v := &authvalidator.Auth{}
			v.InitValidator(v)
			return v, nil
		}).
		Submit(func(f *builder.Form, values map[string]any) (res builderdto.FormSubmitResult, err error) {
			var dbAuth *model.AdminAuth
			dbAuth = util.MapKeyValue(values, "dbAuth", dbAuth)
			if dbAuth.Key == "" {
				err = errors.New("数据异常，刷新页面后重新执行此操作")
				return
			}
			isEdit := util.MapKeyValue(values, "is_edit", uint8(0)) == 1
			err = global.MySQL.Transaction(func(tx *gorm.DB) (err error) {
				if isEdit {
					// 修改
					err = global.MySQL.Select(
						"name", "desc", "type", "icon", "page_open", "request_mode", "request_url", "request_method",
						"request_require_params", "updated_at", "parent_key",
					).Save(dbAuth).Error
					if err != nil {
						return
					}
				} else {
					// 新增
					err = global.MySQL.Create(dbAuth).Error
					if err != nil {
						return
					}
					dbChildAuths := util.MapKeyValue(values, "dbChildAuths", []*model.AdminAuth{})
					if len(dbChildAuths) > 0 {
						err = global.MySQL.Create(dbChildAuths).Error
						if err != nil {
							err = fmt.Errorf("创建子权限失败！%v", err)
							return
						}
					}
				}
				// 处理所有权限数据的完整属性值，并清理缓存
				err = data.HandleAllAuthFullAttr()
				return
			})
			// return response.FailJsonData(fmt.Sprintf("测试中！%v", err))
			if err != nil {
				if isEdit {
					err = fmt.Errorf("编辑失败！%v", err.Error())
				} else {
					err = fmt.Errorf("新增失败！%v", err.Error())
				}
				return
			}
			res.Reload = true
			return
		})

	if isEdit || isAddChild {
		formBuilder.Load(func(f *builder.Form) (res any, err error) {
			key := util.QueryParam(ctx, "key", "")
			if key == "" {
				err = fmt.Errorf("数据异常")
				return
			}
			if isAddChild {
				res = dto.AdminAuthPageFormLoadResponse{
					ParentKey: key,
					Type:      s.GetPageType(ctx).GetDbTypeUint8Value(),
				}
			} else {
				var data dto.AdminAuthPageFormLoadResponse
				dbErr := global.MySQL.Model(&model.AdminAuth{}).Where("`key` = ? and state <> ?", key, stateenum.DELETED).First(&data).Error
				if (dbErr != nil && !errors.Is(dbErr, gorm.ErrRecordNotFound)) || data.Key == "" {
					err = fmt.Errorf("数据获取异常！%v", dbErr)
					return
				}
				res = data
			}
			return
		})
	}
	return
}

// 父权限下拉搜索选项
func (s *authPage) ParentKeyDropdownSearchOptions(ctx *gin.Context) (options []builderdto.Option, err error) {
	param := util.QueryParams(ctx)
	query := global.MySQL.Where("state = ?", stateenum.NORMAL).Limit(20).Select("`key`,name,full_name")
	if values, exists := param["values"]; exists {
		if values, ok := values.(string); ok && values != "" {
			query = query.Where("`key` in ?", strings.Split(values, ","))
		}
	}
	if keywords, exists := param["keywords"]; exists {
		if keywords, ok := keywords.(string); ok && keywords != "" {
			likeKeywords := "%" + keywords + "%"
			query = query.Where("(`key` like ? or full_name like ?)", likeKeywords, likeKeywords)
		}
	}
	var items []model.AdminAuth
	dbErr := query.Find(&items).Error
	if dbErr != nil {
		err = fmt.Errorf("数据查询异常！%v", dbErr)
		return
	}
	options = make([]builderdto.Option, 0, len(items))
	for _, item := range items {
		txt := item.FullName
		if txt == "" {
			txt = item.Name
		}
		options = append(options, builderdto.Option{Value: item.Key, Label: item.Key + "【" + txt + "】"})
	}
	return
}
